
// import oimo.collision.*;
// import oimo.collision.broadphase.*;
// import oimo.collision.geometry.*;
// import oimo.common.Transform;
// import oimo.common.Vec3;
// import oimo.dynamics.callback.ContactCallback;
// import oimo.m.Vec3;
// import oimo.m.M;

import { Geometry } from "../../collision/geometry/Geometry";
import { Transform } from "../../common/Transform";
import { Aabb } from "../../collision/geometry/Aabb";
import { ContactCallback } from "../callback/ContactCallback";
import { Vec3 } from "../../common/Vec3";
import { ShapeConfig } from "./ShapeConfig";
import { M } from "../../common/M";
import { RigidBody } from "./RigidBody";
import { Proxy } from "../../collision/broadphase/Proxy";

/**
 * A shape is a component of a rigid body. It attaches a collision geometry to the parent rigid body
 * with some physical properties such as coefficients of friction and restitution. The collision
 * geometry can locally be transformed relative to the parent rigid body's center of gravity.
 */

export class Shape {
	_id: number;

	_prev: Shape;
	_next: Shape;
	_rigidBody: RigidBody;
	_geom: Geometry;

	_localTransform: Transform;
	_ptransform: Transform;
	_transform: Transform;

	_restitution: number;
	_friction: number;
	_density: number;

	_aabb: Aabb;

	_proxy: Proxy;

	_collisionGroup: number;
	_collisionMask: number;

	_contactCallback: ContactCallback;

	displacement: Vec3;

	/**
	 * Extra field that users can use for their own purposes.
	 */
	userData: any;

	/**
	 * Creates a new shape by configuration `config`.
	 */
	constructor(config: ShapeConfig) {
		this._id = -1;

		this._localTransform = new Transform(config.position, config.rotation);
		this._ptransform = this._localTransform.clone();
		this._transform = this._localTransform.clone();

		this._restitution = config.restitution;
		this._friction = config.friction;
		this._density = config.density;
		this._geom = config.geometry;
		this._collisionGroup = config.collisionGroup;
		this._collisionMask = config.collisionMask;
		this._contactCallback = config.contactCallback;

		this._aabb = new Aabb();

		this._proxy = null;
		this.displacement = new Vec3();
	}

	// --- internal ---


	public _sync(tf1: Transform, tf2: Transform): void {
		M.transform_mul(this._ptransform, this._localTransform, tf1);
		M.transform_mul(this._transform, this._localTransform, tf2);

		this._geom._computeAabb(this._aabb, this._ptransform);
		let min = this._aabb.getMin();
		let max = this._aabb.getMax();

		this._geom._computeAabb(this._aabb, this._transform);
		this._aabb._min.minEq(min);
		this._aabb._max.maxEq(max);

		if (this._proxy != null) {
			this._transform._position.subTo(this._ptransform._position, this.displacement);
			this._rigidBody._world._broadPhase.moveProxy(this._proxy, this._aabb, this.displacement);
		}
	}

	// --- public ---

	/**
	 * Returns the coefficient of friction.
	 */
	public getFriction(): number {
		return this._friction;
	}

	/**
	 * Sets the coefficient of friction to `friction`.
	 */
	public setFriction(friction: number): void {
		this._friction = friction;
	}

	/**
	 * Returns the coefficient of restitution.
	 */
	public getRestitution(): number {
		return this._restitution;
	}

	/**
	 * Sets the coefficient of restitution to `restitution`.
	 */
	public setRestitution(restitution: number): void {
		this._restitution = restitution;
	}

	/**
	 * Returns the transform of the shape relative to the parent rigid body's transform.
	 */
	public getLocalTransform(): Transform {
		return this._localTransform.clone();
	}

	/**
	 * Sets `transform` to the transform of the shape relative to the parent rigid body's
	 * transform.
	 *
	 * This does not create a new instance of `Transform`.
	 */
	public getLocalTransformTo(transform: Transform): void {
		transform.copyFrom(this._localTransform);
	}

	/**
	 * Returns the world transform of the shape.
	 */
	public getTransform(): Transform {
		return this._transform.clone();
	}

	/**
	 * Sets `transform` to the world transform of the shape.
	 *
	 * This does not create a new instance of `Transform`.
	 */
	public getTransformTo(transform: Transform): void {
		transform.copyFrom(this._transform);
	}

	/**
	 * Sets the shape's transform to `transform` relative to the parent rigid body's transform.
	 *
	 * This affects the parent rigid body's mass data.
	 */
	public setLocalTransform(transform: Transform): void {
		this._localTransform.copyFrom(transform);
		if (this._rigidBody != null) {
			this._rigidBody._shapeModified();
		}
	}

	/**
	 * Returns the density of the shape.
	 */
	public getDensity(): number {
		return this._density;
	}

	/**
	 * Sets the density of the shape to `density`.
	 *
	 * This affects the parent rigid body's mass data.
	 */
	public setDensity(density: number): void {
		this._density = density;
		if (this._rigidBody != null) {
			this._rigidBody._shapeModified();
		}
	}

	/**
	 * Returns the AABB of the shape. The AABB may be incorrect if the shape doesn't have a
	 * parent rigid body.
	 */
	public getAabb(): Aabb {
		return this._aabb.clone();
	}

	/**
	 * Sets `aabb` to the AABB of the shape. The AABB may be incorrect if the shape doesn't have a
	 * parent rigid body.
	 *
	 * This does not create a new instance of `AABB`.
	 */
	public getAabbTo(aabb: Aabb): void {
		aabb.copyFrom(this._aabb);
	}

	/**
	 * Returns the colision geometry of the shape.
	 */
	public getGeometry(): Geometry {
		return this._geom;
	}

	/**
	 * Returns the parent rigid body. This returns `null` if the shape doesn't have a parent
	 * rigid body.
	 */
	public getRigidBody(): RigidBody {
		return this._rigidBody;
	}

	/**
	 * Returns the collision group bits the shape belongs to.
	 */
	public getCollisionGroup(): number {
		return this._collisionGroup;
	}

	/**
	 * Sets the shape's collision group bits to `collisionGroup`.
	 */
	public setCollisionGroup(collisionGroup: number): void {
		this._collisionGroup = collisionGroup;
	}

	/**
	 * Returns the collision mask bits of the shape.
	 */
	public getCollisionMask(): number {
		return this._collisionMask;
	}

	/**
	 * Sets the shape's collision mask bits to `collisionMask`.
	 */
	public setCollisionMask(collisionMask: number): void {
		this._collisionMask = collisionMask;
	}

	/**
	 * Returns the contact callback of the shape.
	 */
	public getContactCallback(): ContactCallback {
		return this._contactCallback;
	}

	/**
	 * Sets the contact callback of the shape to `callback`.
	 */
	public setContactCallback(callback: ContactCallback): void {
		this._contactCallback = callback;
	}

	/**
	 * Returns the previous shape in the rigid body.
	 *
	 * If the previous one does not exist, `null` will be returned.
	 */
	public getPrev(): Shape {
		return this._prev;
	}

	/**
	 * Returns the next shape in the rigid body.
	 *
	 * If the next one does not exist, `null` will be returned.
	 */
	public getNext(): Shape {
		return this._next;
	}

}
